var system = require('system');
var webpage = require('webpage');
var webserver = require('webserver');


Date.prototype.format = function(format) {
    var o = {
        "M+": this.getMonth() + 1, //month
        "d+": this.getDate(),    //day
        "h+": this.getHours(),   //hour
        "m+": this.getMinutes(), //minute
        "s+": this.getSeconds(), //second
        "q+": Math.floor((this.getMonth() + 3) / 3),  //quarter
        "S": this.getMilliseconds() //millisecond
    };
    if (/(y+)/.test(format)) format = format.replace(RegExp.$1,
        (this.getFullYear() + "").substr(4 - RegExp.$1.length));
    for (var k in o)if (new RegExp("(" + k + ")").test(format))
        format = format.replace(RegExp.$1,
            RegExp.$1.length == 1 ? o[k] :
                ("00" + o[k]).substr(("" + o[k]).length));
    return format;
};

console.debug = function(){};
logger_log = function (log) {
    console.log('[' + new Date().format('yyyy-MM-dd hh:mm:ss') + '] ' + log);
};


var port, server, service;

if (system.args.length < 3) {
    logger_log('Usage: phantom.js <portnumber> <servertype>');
    phantom.exit(1);
} else {
    port = system.args[1];
    server_type = system.args[2];
    server_proxy = 'localhost';

    server = webserver.create();

    service = server.listen(port, {
        'keepAlive': false
    }, function (request, response) {
        phantom.clearCookies();

        //console.debug(JSON.stringify(request, null, 4));
        //check method
        if (request.method == 'GET') {
            if (request.url == '/settings') {
                body = 'server_type: ' + server_type + '\nserver_proxy: ' + server_proxy;
                response.statusCode = 200;
            } else {
                body = 'url or method error!';
                response.statusCode = 403;
            }
            response.headers = {
                'Cache': 'no-cache',
                'Content-Length': body.length
            };
            response.write(body);
            response.closeGracefully();
            return;
        } else {
            //get request body
            try {
                var fetch = JSON.parse(request.post);
                //var fetch = JSON.parse(request.postRaw);
            } catch (e) {
                body = 'post data must be json string';
                logger_log(body);
                response.headers = {
                    'Cache': 'no-cache',
                    'Content-Length': body.length
                };
                response.write(body);
                response.closeGracefully();
                return;
            }
        }

        var first_response = null,
            finished = false,
            page_loaded = false,
            start_time = Date.now(),
            end_time = null,
            script_executed = false,
            script_result = null,
            wait_before_end = fetch.wait_before_end || 1,
            load_css = fetch.load_css || false;

        if ('settings' in fetch) {
            body = '';
            if ('server_type' in fetch.settings){
                server_type = fetch.settings['server_type'];
                body += 'server_type: ' + server_type + '\n';
            }
            if ('proxy' in fetch.settings) {
                server_proxy = fetch.settings['proxy']['host'] + ':' + fetch.settings['proxy']['port'];
                phantom.setProxy(fetch.settings['proxy']['host'], fetch.settings['proxy']['port'],
                    'manual', fetch.settings['proxy']['username'], fetch.settings['proxy']['password']);
                body += server_proxy + '\n';
            }
            response.statusCode = 200;
            response.headers = {
                'Cache': 'no-cache',
                'Content-Length': body.length
            };
            response.write(body);
            response.closeGracefully();
            logger_log(body);
            return;
        }

        //set proxy
        if (server_type == 'dynamic' && 'proxy' in fetch) {
            logger_log('change proxy');
            phantom.setProxy(fetch.proxy['host'], fetch.proxy['port'], 'manual', fetch.proxy['username'], fetch.proxy['password']);
        }

        //create page and set page
        var page = webpage.create();
        page.onConsoleMessage = function(msg) {
            //logger_log('Msg: ' + msg);
        };
        page.viewportSize = {
            width:  fetch.viewport_width || 1024,
            height: fetch.viewport_height || 768
        };
        if (fetch.headers && fetch.headers['User-Agent']) {
            page.settings.userAgent = fetch.headers['User-Agent'];
        }
        //this may cause memory leak
        page.settings.loadImages = fetch.load_images ? fetch.load_images : true;
        page.settings.resourceTimeout = fetch.timeout ? fetch.timeout*1000 : 60*1000;
        if (fetch.headers) {
            page.customHeaders = fetch.headers;
        }

        // add callbacks
        page.onInitialized = function() {
            if (!script_executed && fetch.js_script && fetch.js_run_at === "document-start") {
                script_executed = true;
                logger_log('running document-start script.');
                script_result = page.evaluateJavaScript(fetch.js_script);
            }
        };
        page.onLoadFinished = function(status) {
            page_loaded = true;
            if (!script_executed && fetch.js_script && fetch.js_run_at !== "document-start") {
                script_executed = true;
                logger_log('running document-end script.');
                script_result = page.evaluateJavaScript(fetch.js_script);
            }
            console.debug("waiting "+wait_before_end+"ms before finished.");
            end_time = Date.now() + wait_before_end;
            setTimeout(make_result, wait_before_end+10, page);
        };
        page.onResourceRequested = function(request) {
            console.debug("Starting request: #"+request.id+" ["+request.method+"]"+request.url);
            if (load_css && ((/http:\/\/.+?\.css/gi).test(requestData['url']) || requestData.headers['Content-Type'] == 'text/css')) {
                console.debug('The url of the request is matching. Aborting: ' + requestData['url']);
                request.abort();
            } else {
                end_time = null;
            }
        };
        page.onResourceReceived = function(response) {
            console.debug("Request finished: #"+response.id+" ["+response.status+"]"+response.url);
            if (first_response === null && response.status != 301 && response.status != 302) {
                first_response = response;
            }
            if (page_loaded) {
                console.debug("waiting "+wait_before_end+"ms before finished.");
                end_time = Date.now() + wait_before_end;
                setTimeout(make_result, wait_before_end+10, page);
            }
        };
        page.onResourceError = page.onResourceTimeout=function(response) {
            console.info("Request error: #"+response.id+" ["+response.errorCode+"="+response.errorString+"]"+response.url);
            if (first_response === null) {
                first_response = response;
            }
            if (page_loaded) {
                console.debug("waiting "+wait_before_end+"ms before finished.");
                end_time = Date.now() + wait_before_end;
                setTimeout(make_result, wait_before_end+10, page);
            }
        };

        // make sure request will finished
        setTimeout(function(page) {
            make_result(page);
        }, page.settings.resourceTimeout + 100, page);

        // send request
        page.open(fetch.url, {
            operation: fetch.method,
            data: fetch.data
        });

        // make response
        function make_result(page) {
            if (finished) {
                return;
            }
            if (Date.now() - start_time < page.settings.resourceTimeout) {
                if (!!!end_time) {
                    return;
                }
                if (end_time > Date.now()) {
                    setTimeout(make_result, Date.now() - end_time, page);
                    return;
                }
            }

            var result = {};
            try {
                result = _make_result(page);
            } catch (e) {
                result = {
                    //orig_url: fetch.url,
                    status_code: 599,
                    error: e.toString(),
                    content:  '',
                    headers: {},
                    url: page.url,
                    cookies: {},
                    time: (Date.now() - start_time) / 1000,
                    save: fetch.save
                }
            }

            page.close();
            finished = true;

            var body = JSON.stringify(result, null, 2);
            logger_log("["+result.status_code+"] "+fetch.url+"\t"+result.time+"\t"+body.length);
            response.writeHead(200, {
                'Cache': 'no-cache',
                'Content-Type': 'application/json'
            });
            response.write(body);
            response.closeGracefully();
        }

        function _make_result(page) {
            if (first_response === null) {
                throw "No response received!";
            }

            var cookies = {};
            page.cookies.forEach(function(e) {
                cookies[e.name] = e.value;
            });

            var headers = {};
            if (first_response.headers) {
                first_response.headers.forEach(function(e) {
                    headers[e.name] = e.value;
                });
            }

            return {
                //orig_url: fetch.url,
                status_code: first_response.status || 599,
                error: first_response.errorString,
                content:  page.content,
                headers: headers,
                url: page.url,
                cookies: cookies,
                time: (Date.now() - start_time) / 1000,
                js_script_result: script_result,
                save: fetch.save
            }
        }
    });

    if (service) {
        logger_log('Phantom server running on port ' + port);
    } else {
        logger_log('Error: Could not create phantom server listening on port ' + port);
        phantom.exit();
    }
}

